ROS2 话题通信
话题概念
在 ROS 2 中,**话题(Topic)**是节点之间进行单向、异步消息通信的重要机制。
话题数据传输的特性是从一个节点到另外一个节点,发送数据的对象称之为发布者,接收数据的对象称之为订阅者,每一个话题都需要有一个名字,传输的数据也需要有固定的数据类型。
- 发布者负责向某个话题发送消息;
- 订阅者负责从某个话题接收消息;
- 只要话题名称一致且消息类型匹配,节点之间就可以完成通信。
text
发布者节点 → 话题 (Topic) → 订阅者节点说明示例 发布者不断发送字符串 "Hello ROS 2";订阅者监听同一个话题并打印收到的字符串。 显然,话题通信中需要至少两个节点,一个为发布者节点,一个为订阅者节点。
话题通信的特点
ROS 2 话题通信具有以下特点:
- 异步通信:发送方和接收方无需同步阻塞等待;
- 一对多 / 多对多:一个发布者可对应多个订阅者,一个话题也可被多个发布者使用;
- 解耦合:节点之间不需要直接知道对方程序细节,只需约定好话题名和消息类型。
异步的含义
异步表示发布者发布消息后,订阅者并不一定立即接收到消息。即不是同步通信。
Python 发布者/订阅者程序的基本构成
- 导入 rclpy;
- 导入 Node 基类;
- 导入消息类型,例如 std_msgs.msg.String;
- 定义节点类;
- 在节点中创建 publisher 或 subscription;
- 编写回调函数;
- 在 main() 中初始化、创建节点并执行 spin()。
学习内容五:话题通信实践
(一)创建实验功能包
进入工作空间 src 目录,创建 Python 功能包:
bash
ros2 pkg create learn_topic --build-type ament_python --dependencies rclpy std_msgs说明注意 这里依赖多了一个
std_msgs,std_msgs是 ROS 2 常用标准消息包。接下来的部分要使用String消息类型。
(二)编写发布者节点
回调函数:用于处理订阅消息、服务请求、定时器触发等事件的核心机制。ROS 2 使用基于 rclcpp(C++)或 rclpy(Python)的异步执行模型,通过回调函数响应各种事件。主要有订阅者(Subscriber)回调,定时器(Timer)回调,服务(Service)回调。
python
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, node_name:str, topic_name: str):
super().__init__(node_name)
# 由于节点时继承自Node类,在Node类中已经实现了通过create_publisher方法来创建一个发布者
# msg_type表示传递的消息类型,topic表示话题通信的名称,对于发布者和订阅者要统一,qos_profile设置为10即可,不用深究
self.publisher_ = self.create_publisher(msg_type=String,topic=topic_name,qos_profile=10) # 创建发布者对象
# 创建一个定时器,表示每0.5秒执行一次timer_callback这个回调函数,也就是我们要实现的发布者要发布的消息都在回调函数中实现
self.timer_ = self.create_timer(timer_period_sec=0.5, callback = self.timer_callback)
self.i = 0
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = f'Hello ROS 2: [{self.i}]' # 填充消息对象中的消息数据
self.publisher_.publish(msg) # 发布话题消息
self.get_logger().info('已发布消息: "%s"' % msg.data) # 输出日志信息,提示已经完成话题发布
self.i += 1
def main(args = None):
rclpy.init(args=args)
pub_node = PublisherNode(node_name='pub',topic_name='learn_topic') # 节点名 pub,话题名 learn_topic(要和订阅者一致)
rclpy.spin(pub_node) # 启动节点
pub_node.destroy_node()
rclpy.shutdown()(三)编写订阅者节点
订阅者通过 create_subscription() 订阅话题,并在回调函数中处理接收到的消息。
python
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, node_name:str, topic_name: str):
super().__init__(node_name) # ROS2节点父类初始化
self.sub = self.create_subscription(
String, topic_name, self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
def listener_callback(self, msg: String): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info(f'订阅到的数据:{msg.data}') # 输出日志信息,提示订阅收到的话题消息
def main(args=None):
rclpy.init(args=args)
sub_node = SubscriberNode(node_name='sub',topic_name='learn_topic')
rclpy.spin(sub_node) # 启动节点
sub_node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()(四)修改 setup.py
python
entry_points={
'console_scripts': [
'publisher = learn_topic.node_pub:main',
'subscriber = learn_topic.node_sub:main',
],
},(五)编译功能包
返回工作空间根目录,编译并加载环境:
bash
colcon build
source install/setup.bash(六)运行发布者与订阅者节点
1. 运行发布者节点
打开第一个终端:
bash
cd lq_ws/
source install/setup.bash
ros2 run learn_topic publisher2. 运行订阅者节点
第一个终端不关闭,打开第二个终端:
bash
cd lq_ws/
source install/setup.bash
ros2 run learn_topic subscriber运行结果如下:
使用命令行工具验证话题通信
1. 查看当前话题列表
bash
ros2 topic list2. 查看话题消息内容
bash
ros2 topic echo /learn_topic # 注意替换为自己的消息名称将持续显示发布者发送的消息内容。
3. 查看话题信息
bash
ros2 topic info /learn_topic可查看该话题的消息类型以及发布者/订阅者数量。